// 先定义三个常量表示状态
var PENDING = 'pending';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';
// https://www.ituring.com.cn/article/66566

class MyPromise {
  status = PENDING
  callbacks = [];
  constructor(executer) {
    this.value = undefined;
    if (typeof executer === 'function') {
      executer(this.resolved, this.rejected); 
    }
  }

  then(onFulfilled, onRejected) {
    if (this.status === PENDING) {
      return new MyPromise((resolve, reject) => {
        this.callbacks.push(() => {
          let fn = this.status === FULFILLED ? onFulfilled : onRejected;
          try {
            let res = typeof fn === "function" ? fn(this.value) : undefined;
            resolve(res); 
          } catch (error) {
            reject(error)
          }
        })
      })
    } else {
      let fn = this.status === FULFILLED ? onFulfilled : onRejected;
      try {
        let res = typeof fn === "function" ? fn(this.value) : undefined;
        if (res.then) {
          return res;
        } else {
          return new MyPromise(resolve => resolve(res))
        }
      } catch(err) {
        return MyPromise.reject(err);
      }
    }
  }

  static reject(err) {
    return new MyPromise((_, reject) => reject(err));
  }

  static resolve(val) {
    return new MyPromise(resolve => resolve(val));
  }

  resolveP = (p, x) => {
    if (p === x) {
      p.resolved(new TypeError('same object'))
    } else if (x instanceof MyPromise) {
      if (x.status === PENDING) {
        x.then((val) => {
          p.resolved(val);
        }, err => {
          p.rejected(err);
        })
      } else {
        let fn = x.status === FULFILLED ? p.resolved : p.rejected;
        fn(x.value);
      }
    } else if (x && (typeof x === "object" || typeof x === "function") ) {
      let then;
      try {
        then = x.then;
      } catch (error) {
        p.rejected(error);
      }
      if (typeof then === "function") {
        try {
          then.call(x, (val) => {
            if (p.status !== PENDING) {
              this.resolveP(p, val); 
            }
          }, (error) => {
            if (p.status !== PENDING) {
              p.rejected(error);
            }
          })
        } catch(err) {
          if (p.status !== PENDING) {
            p.rejected(err)
          }
        }
      } else {
        p.resolved(x);
      }
    } else {
      p.resolved(x)
    }
    return p;
  }

  resolved = (value) => {
    if (this.status !== PENDING) {
      return;
    }
    this.value = value;
    this.status = FULFILLED;
    setTimeout(() => {
      for (const cb of this.callbacks) {
        cb(value);
      }
    }, 0);
  }
  rejected = (error) => {
    if (this.status !== PENDING) {
      return;
    }
    this.value = error;
    this.status = REJECTED;
    setTimeout(() => {
      for (const cb of this.callbacks) {
        cb(error);
      }
    }, 0);
  }
}



MyPromise.deferred = function () {
  var result = {};
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
}

module.exports = MyPromise;